use crate::{
    errors::cons_error,
    models::{
        user::{NormalUserModel, UserRole},
        user_application::UserApplicationModelDTO,
        ClubFormStatusFlowType,
    },
    services::form_service::FormService,
    utils::{
        authorize::{check_user_has_permission, check_user_has_permission_with_object_id},
        path_params::PathParamMDB,
    },
    AppError, AppResult,
};
use actix_http::StatusCode;
use actix_web::{
    web::{self, ServiceConfig},
    HttpResponse,
};
mod req_struct {
    use serde::{Deserialize, Serialize};
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct ChangeApplicationCtlReq {
        pub status_id: String,
    }
}
/// ## change_application_status
/// 修改提交状态，对于草稿状态的提交只有创建者可以修改到下一个状态
/// 对于非草稿状态则只能由社长修改状态
async fn change_application_status(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    path_params: PathParamMDB,
    req: web::Json<req_struct::ChangeApplicationCtlReq>,
) -> AppResult<HttpResponse> {
    let application_id = get_path_param!(
        path_params,
        application_id,
        MSG_REQUIRE_APPLICATION_ID_IN_PATH
    );

    let application = form_service
        .get_user_application_detail(application_id)
        .await?;
    if application
        .status
        .map(|f| f.flow_type == ClubFormStatusFlowType::Draft)
        .unwrap_or(false)
    {
        // 草稿状态下只需要校验操作者是创建者，草稿状态下只有初状态可以选择，所以不会出现
        if application.user_id.to_string() != user.id.to_string() {
            return Err(AppError::Status(
                StatusCode::FORBIDDEN,
                cons_error::MSG_REQUIRE_AUTHORIZE.into(),
            ));
        }
        let res = form_service
            .change_application_status(application_id, user, &req.status_id)
            .await?;
        return Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(res)));
    }

    // 非草稿状态则需要检查是否为社长或者管理员
    let form_model = form_service.form_detail(application.form_id).await?;
    if !check_user_has_permission(
        &user.clone().into(),
        form_model.club_id.to_string(),
        UserRole::LeaderOrAdmin as u8,
    ) {
        return Err(AppError::Status(
            StatusCode::FORBIDDEN,
            cons_error::MSG_REQUIRE_AUTHORIZE.into(),
        ));
    }
    let res = form_service
        .change_application_status(application_id, user, &req.status_id)
        .await?;
    Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(res)))
}

async fn get_user_application_detail(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    path_params: PathParamMDB,
) -> AppResult<HttpResponse> {
    let application_id = get_path_param!(
        path_params,
        application_id,
        MSG_REQUIRE_APPLICATION_ID_IN_PATH
    );
    let application_detail = form_service
        .get_user_application_detail(application_id)
        .await?;
    let form_detail = form_service.form_detail(application_detail.form_id).await?;
    if application_detail.user_id.to_string() != user.id.to_string()
        && !check_user_has_permission(
            &user.into(),
            form_detail.club_id.to_string(),
            UserRole::LeaderOrAdmin as u8,
        )
    {
        return Err(AppError::Status(
            StatusCode::FORBIDDEN,
            cons_error::MSG_REQUIRE_AUTHORIZE.into(),
        ));
    }
    Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(application_detail)))
}

/// ## archive_application
/// 将申请归档
async fn archive_application(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    path_params: PathParamMDB,
) -> AppResult<HttpResponse> {
    let application_id = get_path_param!(
        path_params,
        application_id,
        MSG_REQUIRE_APPLICATION_ID_IN_PATH
    );
    let application = form_service.application_detail(application_id).await?;
    let form = form_service.form_detail(application.form_id).await?;
    // 验证操作者是否为表单持有者
    if !check_user_has_permission_with_object_id(
        user.clone(),
        form.club_id,
        UserRole::LeaderOrAdmin,
    ) {
        return Err(AppError::Status(
            StatusCode::UNAUTHORIZED,
            cons_error::MSG_REQUIRE_AUTHORIZE.into(),
        ));
    }

    let res = form_service
        .archive_application(application_id, user)
        .await?;

    Ok(HttpResponse::Ok().json(UserApplicationModelDTO::from(res)))
}

pub fn configure_application_controller(config: &mut ServiceConfig) {
    config.service(
        web::scope("/single/{application_id}")
            .service(web::resource("").route(web::get().to(get_user_application_detail)))
            .service(web::resource("/status").route(web::put().to(change_application_status)))
            .service(web::resource("/archive").route(web::post().to(archive_application))),
    );
}
